package com.chenfan.process.biz;

import com.alibaba.fastjson.JSONObject;
import com.chenfan.ccp.common.exception.ApiException;
import com.chenfan.infra.context.UserContextHolder;
import com.chenfan.process.common.constants.ContainerConstants;
import com.chenfan.process.common.exception.ProcessResultCode;
import com.chenfan.process.dto.ApprovalMapDTO;
import com.chenfan.process.entity.po.*;
import com.chenfan.process.entity.vo.ProcessDetailActiveVO;
import com.chenfan.process.entity.vo.ProcessDetailVo;
import com.chenfan.process.enums.InstanceEnums;
import com.chenfan.process.enums.ProcessEnums;
import com.chenfan.process.enums.ProcessResultTypeEnums;
import com.chenfan.process.service.*;
import com.chenfan.process.service.impl.ApprovalSnapshotDomainService;
import com.chenfan.process.vo.ApprovalVo;
import com.chenfan.process.vo.EntireProcessDetailVo;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author liuming
 * @date 2021/10/8
 */
@Service
public class ProcessBizQueryImpl implements ProcessBizQuery {
    @Autowired
    private ApprovalService approvalService;
    @Autowired
    private ProcessDetailService processDetailService;
    @Autowired
    private ProcessInstanceApproveService processInstanceApproveService;

    @Autowired
    protected ApprovalSnapshotDomainService approvalSnapshotPoService;
    @Autowired
    private ApprovalSnapshotPoService approvalSnapshotService;
    @Autowired
    private ProcessFormNodeRelationService processFormNodeRelationService;
    @Autowired
    private ProcessRelatedService processRelatedService;
    @Autowired
    private ProcessFormAttributeService processFormAttributeService;
    @Autowired
    private ApprovalTemplateService approvalTemplateService;

    @Autowired
    protected TemplateConfigService templateConfigService;

    @Override
    public ProcessDetailActiveVO getProcessDetailList(ApprovalMapDTO approval) {
        Collection<Long> approvalIdList = approval.getApprovalIdList();
        // 查询出最后一个审批状态不为拒绝的
        List<ApprovalPo> approvalList = approvalService.listApprovalListWithoutOption(approvalIdList);
        Optional<ApprovalPo> approvalOptional =
            approvalList.stream().filter(e -> ProcessEnums.APPROVING.getCode().equals(e.getStatus())).findAny();

        if (!approvalOptional.isPresent()) {
            // 查询一下审批的状态
            ProcessDetailActiveVO build = ProcessDetailActiveVO.builder().finish(Boolean.TRUE).showButton(Boolean.FALSE)
                .processList(Collections.emptyList()).build();
            if (!CollectionUtils.isEmpty(approvalList)) {
                ApprovalPo approvalPo = approvalList.get(approvalList.size() - 1);
                boolean refuse = ProcessEnums.REFUSE.getCode().equals(approvalPo.getStatus())
                    || ProcessEnums.REVOKE.getCode().equals(approvalPo.getStatus());
                build.setRefuse(refuse);
            }
            return build;
        }
        ApprovalPo approvalPo = approvalOptional.get();

        List<ProcessDetailVo> processDetail =
            getAllProcessDetailList(Collections.singletonList(approvalPo.getApprovalId()));
        if (CollectionUtils.isEmpty(processDetail)) {
            return null;
        }
        Map<Long, List<ProcessDetailVo>> processDetailMap =
            processDetail.stream().collect(Collectors.groupingBy(ProcessDetailVo::getProcessId));

        AtomicReference<Boolean> refuseReference = new AtomicReference<>(Boolean.FALSE);
        List<ProcessDetailActiveVO.ProcessDetailItem> processDetailItemList =
            processDetailMap.entrySet().stream().map(e -> {
                ProcessDetailActiveVO.ProcessDetailItem processDetailItem =
                    new ProcessDetailActiveVO.ProcessDetailItem();
                Long id = e.getKey();
                List<ProcessDetailVo> value = e.getValue();
                // 为父节点或者为创建的节点
                boolean activeNode = value.stream().anyMatch(el -> {
                    if (ProcessResultTypeEnums.REFUSED.getIndex().equals(el.getResultType())
                        || ProcessResultTypeEnums.REVOKE.getIndex().equals(el.getResultType())) {
                        refuseReference.set(Boolean.TRUE);
                    }
                    return ProcessResultTypeEnums.processing(el.getResultType());
                });
                processDetailItem.setId(id);
                processDetailItem.setActive(activeNode);
                return processDetailItem;
            }).collect(Collectors.toList());

        // 查询当前执行的
        ProcessDetailVo processDetailVo = processDetail.get(processDetail.size() - 1);
        ProcessDetailActiveVO processDetailActive =
            ProcessDetailActiveVO.builder().processList(processDetailItemList).build();

        Long userId = UserContextHolder.getUserVo().getUserId();
        processDetailActive.setRefuse(refuseReference.get());
        // 查询当前的节点信息
        // TODO 作为标准, 查询当前有哪几个人审批
        List<ProcessInstanceApprovePo> instances =
            processInstanceApproveService.getApproval(approvalPo.getApprovalId());
        processDetailActive.setShowButton(Boolean.FALSE);

        if (CollectionUtils.isEmpty(instances)) {
            return processDetailActive;
        }
        for (ProcessInstanceApprovePo processInstanceApprovePo : instances) {
            if (processInstanceApprovePo.getUserId().equals(userId)
                && Boolean.FALSE.equals(processDetailActive.getFinish())
                && Boolean.FALSE.equals(processDetailActive.getRefuse())) {
                processDetailActive.setShowButton(Boolean.TRUE);
            }
        }
        if (ProcessEnums.APPROVING.getCode().equals(approvalPo.getStatus())) {
            String symbol = instances.get(0).getSymbol();

            String userName =
                instances.stream().map(e -> String.format("%s", e.getUserName())).collect(Collectors.joining(symbol));
            processDetailActive.setApprovalUserName(userName);
            Map<Long, ProcessInstanceApprovePo> instanceMap =
                instances.stream().collect(Collectors.toMap(ProcessInstanceApprovePo::getUserId, e -> e, (e1, e2) -> e2));

            Long processId = processDetailVo.copyProcessDetailPo().getNextProcessId();
            // 参数不为空,且
            if (Objects.nonNull(processId) && instanceMap.containsKey(userId)) {

                Long groupId = processDetailVo.copyProcessDetailPo().getGroupId();
                List<ProcessRelatedPo> relatedPoList = null;
                if (Objects.nonNull(groupId)) {
                    relatedPoList = processRelatedService.listWithGroup(approvalPo.getApprovalTemplateId(), approvalPo.getVersion(),
                            groupId);
                } else {
                    relatedPoList =
                            processRelatedService.listProcessRelatedAfterProcessId(approvalPo.getApprovalTemplateId(),
                                    approvalPo.getVersion(), processDetailVo.copyProcessDetailPo().getProcessId());
                }
                List<Long> processList = relatedPoList.stream().map(ProcessRelatedPo::getProcessAfterId).distinct()
                        .collect(Collectors.toList());

                List<ProcessFormNodeRelation> editableNodeRelations = processFormNodeRelationService
                    .listNodeAttributes(approvalPo.getApprovalTemplateId(), processList, approvalPo.getVersion());
                if (!CollectionUtils.isEmpty(editableNodeRelations)) {
                    Long formId = editableNodeRelations.get(0).getFormId();
                    List<Long> formAttributes = editableNodeRelations.stream()
                        .map(ProcessFormNodeRelation::getFormAttributeId).collect(Collectors.toList());
                    Map<Long, ProcessFormAttribute> attributeMap =
                        processFormAttributeService.listByFormIdAndAttribute(formId, formAttributes).stream()
                            .collect(Collectors.toMap(ProcessFormAttribute::getId, e -> e));
                    JSONObject jsonObject = new JSONObject();
                    JSONObject requiredObject = new JSONObject();

                    editableNodeRelations.forEach(e -> {
                        ProcessFormAttribute processFormAttribute = attributeMap.get(e.getFormAttributeId());

                        if (e.editable()) {
                            if (Objects.nonNull(processFormAttribute)) {
                                jsonObject.put(processFormAttribute.getFieldKey(), true);
                            }
                        }
                        if (e.required()) {
                            if (Objects.nonNull(processFormAttribute)) {
                                requiredObject.put(processFormAttribute.getFieldKey(), true);
                            }
                        }
                    });
                    if (!jsonObject.isEmpty()) {
                        processDetailActive.setEditableParam(JSONObject.toJSONString(jsonObject));

                    }
                    if (!requiredObject.isEmpty()) {
                        processDetailActive.setCheckParam(JSONObject.toJSONString(requiredObject));

                    }
                }
            }
        }

        return processDetailActive;

    }

    @Override
    public List<List<EntireProcessDetailVo>> getAllProcessList(Collection<Long> approvalIdList)
        throws ExecutionException, InterruptedException {
        CompletableFuture<Map<Long, ApprovalPo>> approvalFuture =
            CompletableFuture.supplyAsync(() -> approvalService.listApprovalListWithoutOption(approvalIdList).stream()
                .collect(Collectors.toMap(ApprovalPo::getApprovalId, e -> e)));
        CompletableFuture<List<ProcessInstanceApprovePo>> approvalInstanceFuture =
            CompletableFuture.supplyAsync(() -> processInstanceApproveService.listApprovalInstance(approvalIdList));

        Map<Long, ApprovalPo> approvalMap = approvalFuture.get();
        List<ProcessInstanceApprovePo> processInstanceApproves = approvalInstanceFuture.get();
        // 分组保持排序
        Map<Long, List<ProcessInstanceApprovePo>> collect = processInstanceApproves.stream().collect(
            Collectors.groupingBy(ProcessInstanceApprovePo::getApprovalId, LinkedHashMap::new, Collectors.toList()));
        Long loginUserId = UserContextHolder.getUserVo().getUserId();
        // 产品需求, 倒序展示
        return collect.entrySet().stream().sorted(Map.Entry.<Long, List<ProcessInstanceApprovePo>>comparingByKey().reversed()).map(o -> {
            List<EntireProcessDetailVo> processDetailList = new ArrayList<>();
            // 初始化创建人节点
            List<ProcessInstanceApprovePo> e = o.getValue();
            ApprovalPo approvalPo = approvalMap.get(o.getKey());

            if (Objects.nonNull(approvalPo)) {
                processDetailList.add(startNode(approvalPo));
            }

            /* TODO 后续做会签的时候,这边可以展示多个人分别审批的情况*/
            LinkedHashMap<Long, List<ProcessInstanceApprovePo>> linkedHashMap = e.stream().collect(Collectors
                .groupingBy(ProcessInstanceApprovePo::getApprovalDetailId, LinkedHashMap::new, Collectors.toList()));

            List<EntireProcessDetailVo> processList = linkedHashMap.entrySet().stream().map(ol -> {
                List<ProcessInstanceApprovePo> instances = ol.getValue();
                EntireProcessDetailVo processDetailVo = new EntireProcessDetailVo();
                processDetailVo.setShowButton(Boolean.FALSE);

                ProcessInstanceApprovePo el = instances.get(0);
                processDetailVo.setApprovalId(el.getApprovalId());
                processDetailVo.setId(el.getId());
                processDetailVo.setCreateTime(el.getCreateTime());

                if (!InstanceEnums.normal(el.getStatus())) {
                    processDetailVo.setCreateTime(el.getUpdateTime());
                }
                processDetailVo.setRemark(el.getRemark());
                processDetailVo.setTransferRemark(el.getTransferRemark());
                // 判断是否为撤回状态
                boolean revoke = instances.stream().anyMatch(ProcessInstanceApprovePo::revoke);
                ProcessResultTypeEnums processStatus = el.getProcessStatus();
                processDetailVo.setResultType(processStatus.getIndex());
                //是否显示催办
                boolean anyMatch = instances.stream().anyMatch(inst -> Objects.equals(inst.getProcessStatus().getIndex(), ProcessResultTypeEnums.CREATED.getIndex()));
                processDetailVo.setShowUrge(anyMatch && Objects.equals(approvalPo.getCreateBy(), loginUserId));
                if(processDetailVo.getShowUrge().booleanValue()) {
                    //判断催办消息是否开启
                    TemplateConfigPo config = templateConfigService.getByTemplateId(approvalPo.getApprovalTemplateId(), approvalPo.getVersion());
                    if(Objects.nonNull(config) && !ObjectUtils.defaultIfNull(config.getRemindNotify(), Boolean.TRUE).booleanValue()) {
                        processDetailVo.setShowUrge(false);
                    }
                    //是否自动发待办是否勾选
                    if(Objects.nonNull(config) && !ObjectUtils.defaultIfNull(config.getAutoNotify(), Boolean.TRUE).booleanValue()) {
                        processDetailVo.setShowUrge(false);
                    }
                }
                if (revoke) {
                    processDetailVo.setExecutorUserName(approvalPo.getUpdateName());
                    return processDetailVo;
                }
                if (instances.size() > 1) {
                    return multiple(instances, processDetailVo, loginUserId);
                }
                if (!processStatus.equals(ProcessResultTypeEnums.CREATED) && !el.getIsSkip()) {
                    processDetailVo.setExecutorUserName(el.getUpdateName());
                } else {
                    processDetailVo.setShowButton(Objects.equals(el.getUserId(), loginUserId));
                    processDetailVo.setExecutorUserName(el.getUserName());
                }
                return processDetailVo;
            }).collect(Collectors.toList());

            processDetailList.addAll(processList);
            return processDetailList;
        }).collect(Collectors.toList());
    }

    @Override
    public List<ApprovalVo> getInstanceStatusList(String serviceName, String approvalTemplateCode, Long userId,
        Integer pageNum, Integer pageSize) {
        return listInstanceStatusList(serviceName, approvalTemplateCode, Collections.singletonList(userId), pageNum, pageSize);
    }

    @Override
    public List<ApprovalVo> listInstanceStatusList(String serviceName, String approvalTemplateCode,
                                                   List<String> approvalTemplateCodeList,
                                                   List<Long> userIdList, Integer pageNum, Integer pageSize) {
        ApprovalPo approvalPo = new ApprovalPo();
        approvalPo.setServiceName(serviceName);
        approvalPo.setStatus(ProcessEnums.APPROVING.getCode());

        // 模板id换取参数
        ApprovalTemplatePo approvalTemplatePo = approvalTemplateService.getByCode(approvalTemplateCode);
        if (Objects.isNull(approvalTemplatePo)) {
            throw new ApiException(ProcessResultCode.TEMPLATE_NOT_EXISTS);
        }

        List<Long> templateIdList = Collections.emptyList();

        if (!CollectionUtils.isEmpty(approvalTemplateCodeList)) {
            List<ApprovalTemplatePo> approvalTemplatePos = approvalTemplateService.listByCodes(approvalTemplateCodeList);
            templateIdList = approvalTemplatePos.stream().map(e -> e.getId()).collect(Collectors.toList());
        }


        approvalPo.setApprovalTemplateId(approvalTemplatePo.getId());
        // 分页查询
        List<ApprovalPo> approvalList = approvalService.pageApproval(approvalPo, templateIdList, userIdList, pageNum, pageSize);

        if (CollectionUtils.isEmpty(approvalList)) {
            return Collections.emptyList();
        }
        List<Long> approvalId = approvalList.stream().map(ApprovalPo::getApprovalId).collect(Collectors.toList());

        Map<Long, List<ProcessInstanceApprovePo>> instances =
                processInstanceApproveService.listApprovingInstances(approvalId).stream()
                        .collect(Collectors.groupingBy(ProcessInstanceApprovePo::getApprovalId));
        return approvalList.stream().filter(e -> {
            List<ProcessInstanceApprovePo> processInstances = instances.get(e.getApprovalId());
            return processInstances.stream().anyMatch(o -> userIdList.contains(o.getUserId()));
        }).map(e -> {
            ApprovalVo approvalVo = e.copyApprovalVo();
            if (!ProcessEnums.finish(e.getStatus())) {
                List<ProcessInstanceApprovePo> processInstances = instances.get(e.getApprovalId());
                if (!CollectionUtils.isEmpty(processInstances)) {
                    String symbol = processInstances.get(0).getSymbol();
                    approvalVo.setUserId(
                            processInstances.stream().map(o -> o.getUserId().toString()).collect(Collectors.joining(",")));
                    approvalVo.setUserName(
                            processInstances.stream().map(element -> String.format("%s", element.getUserName()))
                                    .collect(Collectors.joining(symbol)));
                }
            }
            return approvalVo;
        }).collect(Collectors.toList());
    }

    @Override
    public Map<Long, String> mapApproval(Collection<Long> approvalIdList) {
        approvalIdList = new HashSet<>(approvalIdList);
        if (CollectionUtils.isEmpty(approvalIdList)) {
            return new HashMap<>(ContainerConstants.DEFAULT_EMPTY_MAP_SIZE);
        }
        List<ApprovalPo> approvalList = approvalService.findApprovalList(approvalIdList);
        Map<Long, List<ProcessInstanceApprovePo>> instances =
            processInstanceApproveService.listApprovingInstances(approvalIdList).stream()
                .collect(Collectors.groupingBy(ProcessInstanceApprovePo::getApprovalId));
        return approvalList.stream().collect(Collectors.toMap(ApprovalPo::getApprovalId, e -> {
            if (ProcessEnums.APPROVING.getCode().equals(e.getStatus())) {
                List<ProcessInstanceApprovePo> processInstances = instances.get(e.getApprovalId());
                if (!CollectionUtils.isEmpty(processInstances)) {
                    String symbol = processInstances.get(0).getSymbol();
                    String names = processInstances.stream().map(element -> String.format("%s", element.getUserName()))
                        .collect(Collectors.joining(symbol));
                    return String.format("待%s审批", names);
                }
            }
            return ProcessEnums.getMessage(e.getStatus());
        }));
    }

    @Override
    public String getApprovalTemplateDiagram(Long approvalId) {
        ApprovalSnapshotPo snapshotPo = approvalSnapshotService.getByApprovalId(approvalId);
        return Objects.isNull(snapshotPo) ? null : snapshotPo.getApprovalTemplateSnapshot();
    }

    private List<ProcessDetailVo> getAllProcessDetailList(Collection<Long> approvalIdList) {
        List<ProcessDetailPo> processDetail = processDetailService.listProcessDetailByApprovalId(approvalIdList);
        return processDetail.stream().map(ProcessDetailPo::copyProcessDetailVo).collect(Collectors.toList());
    }

    private EntireProcessDetailVo startNode(ApprovalPo approvalPo) {
        EntireProcessDetailVo build = EntireProcessDetailVo.builder().id(approvalPo.getId())
            .createTime(approvalPo.getCreateTime()).resultType(ProcessResultTypeEnums.PARENT.getIndex())
            .executorUserName(approvalPo.getCreateName()).approvalId(approvalPo.getApprovalId()).build();
        build.setShowButton(Boolean.FALSE);
        build.setShowUrge(Boolean.FALSE);
        return build;
    }

    private EntireProcessDetailVo multiple(List<ProcessInstanceApprovePo> instances,
        EntireProcessDetailVo processDetailVo, Long loginUserId) {
        ProcessInstanceApprovePo el = instances.get(0);
        boolean approval;

        boolean flag;
        if (el.counterSign()) {
            flag = instances.stream().allMatch(ProcessInstanceApprovePo::isPass);
            // 只要有人是审批中就是审批中
            approval = instances.stream().anyMatch(ProcessInstanceApprovePo::normal);
            List<EntireProcessDetailVo.ProcessDetailInstance> processInstances = instances.stream().map(e -> e.ofProcessInstance())
                    .sorted(Comparator.comparing(EntireProcessDetailVo.ProcessDetailInstance::getCreateTime)).collect(Collectors.toList());
            processDetailVo.clearRemark();
            processDetailVo.setTransferRemark(null);
            processDetailVo.setProcessInstances(processInstances);
        } else {
            flag = instances.stream().anyMatch(ProcessInstanceApprovePo::isPass);
            // 只要有人是审批完成就是审批完成
            approval = instances.stream().allMatch(ProcessInstanceApprovePo::normal);
            ProcessInstanceApprovePo any = instances.stream().filter(e -> StringUtils.isNotBlank(e.getRemark())).findAny().orElse(null);
            if(Objects.nonNull(any)) {
                processDetailVo.setRemark(any.getRemark());
            }
            String transferRemark = instances.stream().map(ProcessInstanceApprovePo::getTransferRemark).filter(StringUtils::isNotBlank).collect(Collectors.joining("；</br>"));
            processDetailVo.setTransferRemark(transferRemark);
        }
        if (approval) {
            processDetailVo.setShowButton(instances.stream().filter(ProcessInstanceApprovePo::normal)
                    .anyMatch(element -> Objects.equals(element.getUserId(), loginUserId)));
            processDetailVo.setResultType(ProcessResultTypeEnums.CREATED.getIndex());
        } else {
            // 根据状态来判断. 或签时只要有一个结束就是结束.
            if (flag) {
                processDetailVo.setResultType(ProcessResultTypeEnums.PASS.getIndex());
            } else {
                processDetailVo.setResultType(ProcessResultTypeEnums.REFUSED.getIndex());
            }
            boolean revoke = instances.stream().anyMatch(ProcessInstanceApprovePo::revoke);
            boolean rejected = instances.stream().anyMatch(ProcessInstanceApprovePo::rejected);
            // 有一个撤回就是撤回. 有一个拒绝就是拒绝
            if (revoke) {
                processDetailVo.setResultType(ProcessResultTypeEnums.REVOKE.getIndex());
            }

            if (rejected) {
                processDetailVo.setResultType(ProcessResultTypeEnums.REJECTED.getIndex());
            }
        }

        String userName = instances.stream().map(
            element -> String.format("%s(%s)", element.getUserName(), InstanceEnums.getMessage(element.getStatus())))
            .collect(Collectors.joining(el.getSymbol()));
        processDetailVo.setExecutorUserName(userName);
        return processDetailVo;
    }
}
